/* 
 * Copyright 2009 Shared Object (www.sharedobject.ch)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package edos.prm.project;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

import edos.prm.activity.Activity;
import edos.prm.activity.ActivityManager.PrmActivityManager;
import edos.prm.actor.Actor;
import edos.prm.artifact.Artifact;
import edos.prm.artifact.ArtifactManager;
import edos.prm.artifact.ArtifactSet;
import edos.prm.artifact.AttributePair;
import edos.prm.artifact.Directory;
import edos.prm.artifact.ArtifactManager.PrmArtifactManager;
import edos.prm.engine.PRM;
import edos.prm.engine.PrmSecurityManager;
import edos.prm.project.Project.PrmProject;
import edos.prm.role.RoleException;
import edos.prm.util.Time;
import edos.prm.util.VersionNumber;

/**
 * Project Management Interface allowing to create Projects and retrieve them.
 * @author pawlak
 *
 */
public interface ProjectManager extends Activity {

	/**
	 * Adds a Project to be handled by the ProjectManager
	 * 
	 * @param name the long name of the project
	 * @param description a textual description of the project
	 * @param topicnames the names of the topics associated with this project.
	 * @return project Project to be handled by the ProjectManager
	 * @throws RoleException if the calling task does not have the permission
	 */
	public Project newProject(String name, String description,
			String...topicnames)
	throws RoleException;

	/**
	 * Retrieves all Actors contributing to a Project.
	 * 
	 * @param project Project from which Actors have to be members.
	 * @return a copy of the set of Actors contributing to a Project.
	 */
	public Set<Actor> getActors(Project project) throws RoleException;

	/**
	 * Adds a role for an Actor within a Project
	 * 
	 * @param actor the actor to which the role is being assigned
	 * @param project Project from which Actors have to recieve a Role.
	 * @throws RoleException if the calling task does not have the permission
	 */
	public void addActor(Actor actor, Project project) throws RoleException;

	/**
	 * Searches for Projects corresponding to given characteristics
	 * 
	 * @param attributes characteristics returned Projects have to match
	 * @return a set of Projects corresponding to given characteristics
	 * @throws RoleException if the calling task does not have the permission
	 */
	public Set<Project> find(AttributePair<?>...attributes) throws RoleException;

	/**
	 * Returns all handled Projects handled by the ProjectManager
	 *
	 * @return all handled Projects handled by the ProjectManager
	 * @throws RoleException if the calling task does not have the permission
	 */
	public Set<Project> getProjects() throws RoleException;

	/**
	 * Return the set of topics handled by this project
	 * 
	 * @param project the project whose topics are being queried
	 * @return an array of topics in this project
	 */
	public Set<Topic> getTopics(Project project);

	/**
	 * Return the topic with the given name.
	 * 
	 * @param topicName the name of the topic in String form
	 * @param project the project to which the topic is assigned
	 * @return the topic with the given name
	 * @throws IllegalArgumentException if the topic with that name does not
	 * exist.
	 */
	public Topic getTopicByName(Project project, String topicName)
	throws java.lang.IllegalArgumentException;
	
	/**
	 * Default Java-based implementation of {@code ProjectManager}.
	 */
	final static class PrmProjectManager implements ProjectManager {

		private ArtifactManager am;
		private HashSet<Project> projects;
		private HashMap<Project, HashSet<Actor>> projectToActorMap;
		private String description;
		private Time creationDate;

		private static PrmProjectManager instance;

		private PrmProjectManager() {
			this.projects = new HashSet<Project>();
			this.projectToActorMap = new HashMap<Project, HashSet<Actor>>();
			this.am = PrmArtifactManager.getInstance();
			am.registerArtifactType(PrmProject.class);
			this.description = "Default Project Manager";
			this.creationDate = new Time();
			PrmActivityManager.getInstance().register(this);
		}

		static {
			instance = new PrmProjectManager();
		}

		public static PrmProjectManager getInstance() {
			return instance;
		}

		public void addActor(Actor actor, Project project) {
			PrmSecurityManager.roleCheck("addActor", getClass(), new Class[] {Actor.class, Project.class});
			HashSet<Actor> actors = this.projectToActorMap.get(project);
			if ( actors == null )
				actors = new HashSet<Actor>();
			actors.add(actor);
			this.projectToActorMap.put(project, actors);
		}

		public Project newProject(String name, String descrption,
				String...topicnames) {
			PrmSecurityManager.roleCheck("newProject", getClass(), new Class[] {String.class, String.class, String[].class});
			Project project = new PrmProject(name, description, topicnames);
			this.projects.add(project);
			return project;
		}

		public Set<Project> find(AttributePair<?>...attributes) {
			PrmSecurityManager.roleCheck("find", getClass(), AttributePair[].class);
			HashSet<Project> results = new HashSet<Project>();
			try {
				Directory d = am.lookup(attributes);
				ArtifactSet<Artifact> arts = d.getAllElements();
				for ( Artifact art : arts.getContents() )
					if ( art instanceof Project )
						results.add((Project)art);
			} catch (Exception e) {
				e.printStackTrace();
			}
			return results;
		}

		public Set<Actor> getActors(Project project) {
			PrmSecurityManager.roleCheck("getActors", getClass(), Project.class);
			return project.getContacts();
		}

		public Set<Project> getProjects() {
			PrmSecurityManager.roleCheck("getProjects", getClass());
			HashSet<Project> result = new HashSet<Project>();
			result.addAll(projects);
			return result;
		}

		public Time getCreationDate() {
			return creationDate;
		}

		public String getDescription() {
			return this.description;
		}

		public VersionNumber getVersion() {
			return PRM.getPRMVersion();
		}

		public Set<Topic> getTopics(Project project) {
			PrmSecurityManager.roleCheck("getTopics", getClass(), Project.class);
			return project.getTopics();
		}

		public Topic getTopicByName(Project project, String topicName)
				throws IllegalArgumentException {
			Set<Topic> set = this.getTopics(project);
			for ( Topic topic : set)
				if ( topic.getName().equals(topicName) )
					return topic;
			throw new IllegalArgumentException("Unknown topic: " + topicName);
		}
	}
}
